package com.thinkverse.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.thinkverse.search.constant.ElasticsearchConstants;
import com.thinkverse.search.service.SearchService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 检索服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SearchServiceImpl implements SearchService {
    
    private final ElasticsearchClient elasticsearchClient;
    
    @Override
    public List<Map<String, Object>> fullTextSearch(String keyword, Integer page, Integer size) {
        try {
            // 参数校验
            if (keyword == null || keyword.trim().isEmpty()) {
                log.warn("全文检索关键词为空");
                return new ArrayList<>();
            }
            
            // 限制分页大小
            final int finalSize = Math.min(size, ElasticsearchConstants.MAX_PAGE_SIZE);
            final int finalPage = Math.max(page, 0);
            
            // 构建多字段查询，使用权重提升重要字段的匹配度
            Query multiMatchQuery = MultiMatchQuery.of(m -> m
                .query(keyword)
                .fields("title^3", "abstractText^2", "keywords^2", "authors", "journal")
                .type(co.elastic.clients.elasticsearch._types.query_dsl.TextQueryType.BestFields)
                .fuzziness("AUTO")
            )._toQuery();
            
            // 构建搜索请求
            SearchRequest searchRequest = SearchRequest.of(s -> s
                .index(ElasticsearchConstants.PAPER_INDEX)
                .query(multiMatchQuery)
                .from(finalPage * finalSize)
                .size(finalSize)
                .highlight(h -> h
                    .fields("title", hf -> hf)
                    .fields("abstractText", hf -> hf)
                    .preTags("<em>")
                    .postTags("</em>")
                )
                .sort(so -> so.score(sc -> sc.order(SortOrder.Desc)))
            );
            
            @SuppressWarnings("unchecked")
            SearchResponse<Map<String, Object>> response = 
                (SearchResponse<Map<String, Object>>) (SearchResponse<?>) elasticsearchClient.search(searchRequest, Map.class);
            
            long totalHits = response.hits().total() != null ? response.hits().total().value() : 0;
            log.info("全文检索完成: keyword={}, 返回结果数={}, 总命中数={}", 
                keyword, response.hits().hits().size(), totalHits);
            
            return convertSearchResults(response);
            
        } catch (Exception e) {
            log.error("全文检索失败: keyword={}, page={}, size={}", keyword, page, size, e);
            return new ArrayList<>();
        }
    }
    
    @Override
    public List<Map<String, Object>> advancedSearch(Map<String, Object> searchParams) {
        try {
            List<Query> mustQueries = new ArrayList<>();
            
            // 关键词搜索
            if (searchParams.containsKey("keyword") && searchParams.get("keyword") != null) {
                String keyword = searchParams.get("keyword").toString().trim();
                if (!keyword.isEmpty()) {
                    Query keywordQuery = MultiMatchQuery.of(m -> m
                        .query(keyword)
                        .fields("title^3", "abstractText^2", "keywords")
                    )._toQuery();
                    mustQueries.add(keywordQuery);
                }
            }
            
            // 作者过滤
            if (searchParams.containsKey("author") && searchParams.get("author") != null) {
                String author = searchParams.get("author").toString().trim();
                if (!author.isEmpty()) {
                    Query authorQuery = MatchQuery.of(m -> m
                        .field("authors")
                        .query(author)
                    )._toQuery();
                    mustQueries.add(authorQuery);
                }
            }
            
            // 期刊过滤
            if (searchParams.containsKey("journal") && searchParams.get("journal") != null) {
                String journal = searchParams.get("journal").toString().trim();
                if (!journal.isEmpty()) {
                    Query journalQuery = MatchQuery.of(m -> m
                        .field("journal")
                        .query(journal)
                    )._toQuery();
                    mustQueries.add(journalQuery);
                }
            }
            
            // 年份范围过滤
            if (searchParams.containsKey("startYear") || searchParams.containsKey("endYear")) {
                Integer parsedStartYear = null;
                Integer parsedEndYear = null;
                
                if (searchParams.containsKey("startYear") && searchParams.get("startYear") != null) {
                    try {
                        parsedStartYear = Integer.parseInt(searchParams.get("startYear").toString());
                    } catch (NumberFormatException e) {
                        log.warn("起始年份格式错误: {}", searchParams.get("startYear"));
                    }
                }
                
                if (searchParams.containsKey("endYear") && searchParams.get("endYear") != null) {
                    try {
                        parsedEndYear = Integer.parseInt(searchParams.get("endYear").toString());
                    } catch (NumberFormatException e) {
                        log.warn("结束年份格式错误: {}", searchParams.get("endYear"));
                    }
                }
                
                final Integer startYear = parsedStartYear;
                final Integer endYear = parsedEndYear;
                
                if (startYear != null || endYear != null) {
                    Query rangeQuery = RangeQuery.of(r -> {
                        RangeQuery.Builder builder = r.field("publishYear");
                        if (startYear != null) {
                            builder.gte(co.elastic.clients.json.JsonData.of(startYear));
                        }
                        if (endYear != null) {
                            builder.lte(co.elastic.clients.json.JsonData.of(endYear));
                        }
                        return builder;
                    })._toQuery();
                    mustQueries.add(rangeQuery);
                }
            }
            
            // 研究方向过滤
            if (searchParams.containsKey("researchField") && searchParams.get("researchField") != null) {
                String researchField = searchParams.get("researchField").toString().trim();
                if (!researchField.isEmpty()) {
                    Query fieldQuery = TermQuery.of(t -> t
                        .field("researchField")
                        .value(researchField)
                    )._toQuery();
                    mustQueries.add(fieldQuery);
                }
            }
            
            // 论文类型过滤
            if (searchParams.containsKey("paperType") && searchParams.get("paperType") != null) {
                String paperType = searchParams.get("paperType").toString().trim();
                if (!paperType.isEmpty()) {
                    Query typeQuery = TermQuery.of(t -> t
                        .field("paperType")
                        .value(paperType)
                    )._toQuery();
                    mustQueries.add(typeQuery);
                }
            }
            
            // 如果没有查询条件，返回空结果
            if (mustQueries.isEmpty()) {
                log.warn("高级检索参数为空");
                return new ArrayList<>();
            }
            
            // 构建布尔查询
            Query boolQuery = BoolQuery.of(b -> b
                .must(mustQueries)
            )._toQuery();
            
            // 获取分页参数
            int page = searchParams.containsKey("page") ? 
                Integer.parseInt(searchParams.get("page").toString()) : 0;
            int size = searchParams.containsKey("size") ? 
                Integer.parseInt(searchParams.get("size").toString()) : ElasticsearchConstants.DEFAULT_PAGE_SIZE;
            
            final int finalSize = Math.min(size, ElasticsearchConstants.MAX_PAGE_SIZE);
            final int finalPage = Math.max(page, 0);
            
            SearchRequest searchRequest = SearchRequest.of(s -> s
                .index(ElasticsearchConstants.PAPER_INDEX)
                .query(boolQuery)
                .from(finalPage * finalSize)
                .size(finalSize)
                .highlight(h -> h
                    .fields("title", hf -> hf)
                    .fields("abstractText", hf -> hf)
                    .preTags("<em>")
                    .postTags("</em>")
                )
            );
            
            @SuppressWarnings("unchecked")
            SearchResponse<Map<String, Object>> response = 
                (SearchResponse<Map<String, Object>>) (SearchResponse<?>) elasticsearchClient.search(searchRequest, Map.class);
            
            long totalHits = response.hits().total() != null ? response.hits().total().value() : 0;
            log.info("高级检索完成: 参数={}, 返回结果数={}, 总命中数={}", 
                searchParams, response.hits().hits().size(), totalHits);
            
            return convertSearchResults(response);
            
        } catch (Exception e) {
            log.error("高级检索失败: {}", searchParams, e);
            return new ArrayList<>();
        }
    }
    
    @Override
    public List<Map<String, Object>> hierarchicalSearch(String keyword, String userRole, Integer page, Integer size) {
        try {
            // 参数校验
            if (keyword == null || keyword.trim().isEmpty()) {
                log.warn("分级检索关键词为空");
                return new ArrayList<>();
            }
            
            final int finalSize = Math.min(size, ElasticsearchConstants.MAX_PAGE_SIZE);
            final int finalPage = Math.max(page, 0);
            
            // 构建基础关键词查询
            Query keywordQuery = MultiMatchQuery.of(m -> m
                .query(keyword)
                .fields("title^3", "abstractText^2", "keywords")
            )._toQuery();
            
            // 根据用户角色添加权限过滤
            Query roleQuery;
            if ("ADMIN".equalsIgnoreCase(userRole)) {
                // 管理员可以查看所有数据
                roleQuery = MatchAllQuery.of(m -> m)._toQuery();
            } else if ("RESEARCHER".equalsIgnoreCase(userRole)) {
                // 研究员可以查看已发表和已提交的论文
                Query publishedQuery = TermQuery.of(t -> t
                    .field("status")
                    .value("PUBLISHED")
                )._toQuery();
                Query submittedQuery = TermQuery.of(t -> t
                    .field("status")
                    .value("SUBMITTED")
                )._toQuery();
                roleQuery = BoolQuery.of(b -> b
                    .should(publishedQuery)
                    .should(submittedQuery)
                    .minimumShouldMatch("1")
                )._toQuery();
            } else {
                // 普通用户只能查看已发表的论文
                roleQuery = TermQuery.of(t -> t
                    .field("status")
                    .value("PUBLISHED")
                )._toQuery();
            }
            
            // 构建布尔查询
            Query boolQuery = BoolQuery.of(b -> b
                .must(keywordQuery)
                .must(roleQuery)
            )._toQuery();
            
            SearchRequest searchRequest = SearchRequest.of(s -> s
                .index(ElasticsearchConstants.PAPER_INDEX)
                .query(boolQuery)
                .from(finalPage * finalSize)
                .size(finalSize)
                .highlight(h -> h
                    .fields("title", hf -> hf)
                    .fields("abstractText", hf -> hf)
                    .preTags("<em>")
                    .postTags("</em>")
                )
                .sort(so -> so.score(sc -> sc.order(SortOrder.Desc)))
            );
            
            @SuppressWarnings("unchecked")
            SearchResponse<Map<String, Object>> response = 
                (SearchResponse<Map<String, Object>>) (SearchResponse<?>) elasticsearchClient.search(searchRequest, Map.class);
            
            long totalHits = response.hits().total() != null ? response.hits().total().value() : 0;
            log.info("分级检索完成: keyword={}, userRole={}, 返回结果数={}, 总命中数={}", 
                keyword, userRole, response.hits().hits().size(), totalHits);
            
            return convertSearchResults(response);
            
        } catch (Exception e) {
            log.error("分级检索失败: keyword={}, userRole={}, page={}, size={}", keyword, userRole, page, size, e);
            return new ArrayList<>();
        }
    }
    
    @Override
    public List<String> getSearchSuggestions(String keyword) {
        try {
            // 参数校验
            if (keyword == null || keyword.trim().isEmpty()) {
                return new ArrayList<>();
            }
            
            // 使用前缀查询获取建议
            Query prefixQuery = PrefixQuery.of(p -> p
                .field("title.keyword")
                .value(keyword.toLowerCase())
            )._toQuery();
            
            SearchRequest searchRequest = SearchRequest.of(s -> s
                .index(ElasticsearchConstants.PAPER_INDEX)
                .query(prefixQuery)
                .size(ElasticsearchConstants.SUGGESTION_SIZE)
                .source(sc -> sc.filter(f -> f.includes("title")))
            );
            
            @SuppressWarnings("unchecked")
            SearchResponse<Map<String, Object>> response = 
                (SearchResponse<Map<String, Object>>) (SearchResponse<?>) elasticsearchClient.search(searchRequest, Map.class);
            
            List<String> suggestions = response.hits().hits().stream()
                .map(hit -> {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> source = (Map<String, Object>) hit.source();
                    if (source != null && source.containsKey("title")) {
                        Object title = source.get("title");
                        return title != null ? title.toString() : "";
                    }
                    return "";
                })
                .filter(title -> !title.isEmpty())
                .distinct()
                .limit(ElasticsearchConstants.SUGGESTION_SIZE)
                .collect(Collectors.toList());
            
            log.info("获取搜索建议完成: keyword={}, 建议数={}", keyword, suggestions.size());
            
            return suggestions;
            
        } catch (Exception e) {
            log.error("获取搜索建议失败: keyword={}", keyword, e);
            return new ArrayList<>();
        }
    }
    
    /**
     * 转换搜索结果
     * 将Elasticsearch的Hit对象转换为Map格式，包含源数据、分数和高亮信息
     */
    @SuppressWarnings("unchecked")
    private List<Map<String, Object>> convertSearchResults(SearchResponse<Map<String, Object>> response) {
        List<Map<String, Object>> results = new ArrayList<>();
        
        for (Hit<Map<String, Object>> hit : response.hits().hits()) {
            Map<String, Object> result = new HashMap<>();
            
            // 复制源数据
            if (hit.source() != null) {
                result.putAll((Map<? extends String, ? extends Object>) hit.source());
            }
            
            // 添加相关度分数
            Double score = hit.score();
            if (score != null && !score.isNaN()) {
                result.put("score", score);
            }
            
            // 添加高亮信息
            if (hit.highlight() != null && !hit.highlight().isEmpty()) {
                Map<String, List<String>> highlights = hit.highlight();
                StringBuilder highlightText = new StringBuilder();
                
                if (highlights.containsKey("title")) {
                    highlightText.append("标题: ").append(String.join(" ", highlights.get("title")));
                }
                if (highlights.containsKey("abstractText")) {
                    if (highlightText.length() > 0) highlightText.append(" | ");
                    List<String> abstractHighlights = highlights.get("abstractText");
                    // 只取前3个高亮片段，避免过长
                    String abstractText = abstractHighlights.stream()
                        .limit(3)
                        .collect(Collectors.joining(" "));
                    highlightText.append("摘要: ").append(abstractText);
                }
                
                if (highlightText.length() > 0) {
                    result.put("highlight", highlightText.toString());
                }
            }
            
            results.add(result);
        }
        
        return results;
    }
}
